package appClientModule;

import java.net.*;
import java.io.*;
import java.awt.*;

/**
 * Class for controlling game play ie game dealer
 * 
 * @author Sam Cavenagh
 * @version 6/11/02
 * 
 *          Website: http://home.pacific.net.au/~cavenagh/SH/ Email:
 *          cavenaghweb@hotmail.com
 */
class Dealer {

	// how long ai pause for before making move(int ms)
	int aipause = 1000;

	// used for testing makes deck empty at start of game if true
	boolean fastgame = false;

	// you must play less than a seven if true
	boolean seven = false;

	// if nine is invisible
	boolean nine = false;

	// if can swap cards at start of game
	boolean swap = false;
	int swapcount = 0;

	ServerSocket listenSocket;
	Socket gameSocket[] = new Socket[3];
	PrintWriter out[] = new PrintWriter[3];
	BufferedReader in[] = new BufferedReader[3];

	SHinterface sh;

	Image cardspic;
	Image back;
	Image backSW;
	Image pointer[];
	Image burntPic;

	Graphics g;

	String playersName;

	String otherNames[] = new String[3];

	boolean waitformsg[];
	boolean outofgame[] = new boolean[4];

	PlayerType aiPlayer[] = new PlayerType[3];

	boolean listen = true;

	boolean burnt = false;

	int socketCount = 0;

	int whosturn = 2;

	int position = 1;// ie first, second,third or shithead

	Card deck[] = new Card[52];
	Hand hands[] = new Hand[4];
	Card pile[] = new Card[52];
	GameAI ai;
	ReinforcementLearningAI rlAI;
	private int numReinforcementLearners;

	Point tableposition[][] = new Point[3][3];
	Point centre1;
	Point pointerpoints[] = new Point[4];

	Score score;

	int gameID = 1;// Stops AI playing a move in wrong game


	Dealer(SHinterface sh, Image cardspic, Graphics g, Hand hand, Image back,
			Image backSW, boolean fastgame, boolean seven, boolean nine,
			boolean swap, Image[] pointer, Image burntPic, Score score) {
		this.sh = sh;
		this.cardspic = cardspic;
		this.g = g;
		this.back = back;
		this.backSW = backSW;
		this.fastgame = fastgame;
		this.seven = seven;
		this.nine = nine;
		this.pointer = pointer;
		this.swap = swap;
		this.burntPic = burntPic;
		this.score = score;

		score.imDealer(this);

		hands[0] = new Hand(sh, back, g);
		hands[1] = new Hand(sh, back, g);
		hands[2] = new Hand(sh, back, g);
		hands[3] = hand;

		
		ai = new GameAI(seven, nine);

		outofgame[0] = false;
		outofgame[1] = false;
		outofgame[2] = false;
		outofgame[3] = false;

		waitformsg = new boolean[3];
		for (int n = 0; n < 3; n++) {
			waitformsg[n] = false;
			aiPlayer[n] = PlayerType.HUMAN;
		}

		tableposition[0][0] = new Point(0, 103);
		tableposition[0][1] = new Point(0, 188);
		tableposition[0][2] = new Point(0, 276);

		tableposition[1][0] = new Point(103, 0);
		tableposition[1][1] = new Point(188, 0);
		tableposition[1][2] = new Point(276, 0);

		tableposition[2][0] = new Point(354, 103);
		tableposition[2][1] = new Point(354, 188);
		tableposition[2][2] = new Point(354, 276);

		centre1 = new Point(188, 175);

		pointerpoints[0] = new Point(115, 220);
		pointerpoints[1] = new Point(220, 110);
		pointerpoints[2] = new Point(330, 220);
		pointerpoints[3] = new Point(220, 330);

	}

	public void onePlayer(String playersName) {
		this.playersName = playersName;
		score.addName(playersName);
		
		for (int i = 0; i < numReinforcementLearners; i++) {
			otherNames[i] = "learningAI-" + (i + 1);
			score.addName(otherNames[i]);
			aiPlayer[i] = PlayerType.REINFORCEMENT_LEARNING;
			socketCount++;
		}
		
		for (int n = numReinforcementLearners; n < 3; n++) {
			otherNames[n] = "AI-" + (n + 1);
			score.addName(otherNames[n]);
			aiPlayer[n] = PlayerType.SET_STRATEGY;
			socketCount++;
		}
		
		deal();
	}

	public void start() {
		if (listen && socketCount < 3) {
			for (int n = 0; n < 3; n++)
				if (gameSocket[n] == null) {
					otherNames[n] = "AI" + (n + 1);
					score.addName(otherNames[n]);
					aiPlayer[n] = PlayerType.SET_STRATEGY;
					socketCount++;
				}

			try {
				listenSocket.close();
			} catch (IOException e) {
				sh.addMsg("Error Closing Listen " + e);
			}
		}
	}

	public int getAIPause() {
		return aipause;
	}

	public void setAIPause(int aipause) {
		this.aipause = aipause;
	}
	
	public void setReinforcementLearningAI(ReinforcementLearningAI rlAI) {
		this.rlAI = rlAI;
		//may not be the right place to reset
	}

	public void setNumReinforcementLearners(int numReinforcementLearners) {
		this.numReinforcementLearners = numReinforcementLearners;
	}

	private void shuffle() {
		for (int r = 0; r < 2; r++)
			// number of times pile shuffled
			for (int n = 0; n < 52; n++) {
				int newlocation = (int) Math.round(Math.random() * 51);
				Card temp = deck[newlocation];
				deck[newlocation] = deck[n];
				deck[n] = temp;
			}
	}

	public void createConnection(String playersName) {
		this.playersName = playersName;
		score.addName(playersName);
		new ListenThread();
	}

	private void sendCommand(String command, int socNumber) {
		// sh.addMsg("msg:" + command + " socket:" + socNumber);
		if (socNumber < 3)
			if (gameSocket[socNumber] != null)
				out[socNumber].println(command);
	}

	private void sendMsg(String msg) {
		for (int n = 0; n < 3; n++) {
			if (out[n] != null)
				out[n].println("msg:" + msg);
		}
	}

	private void deal() {
		// adding card to deck
		for (int n = 0; n < 52; n++) {
			Card card = new Card(n + 1, cardspic, sh, g);
			deck[n] = card;
		}
		shuffle();// shuffling card deck
		int player = whosturn;
		for (int n = 0; n < 36; n++) {
			// dealing first card in deck
			if (player != 3)// player 3 is dealer no need to send command
				sendCommand("deal:" + deck[0].getNumber() + ":", player);
			// placing cards in players hand, facedown then faceup.
			hands[player].deal(deck[0]);

			// moving cards in deck forwards
			for (int w = 0; w < 51; w++)
				deck[w] = deck[w + 1];

			// removing card from deck
			deck[51] = null;

			// changing player being dealt to
			player++;
			if (player >= 4)
				player = 0;
		}
		sendDetails();

		if (fastgame)
			for (int n = 0; n < 52; n++)
				deck[n] = null;

		if (swap) {// if performing card swap
			displayTable();
			Card inhand[] = new Card[3];
			Card ontable[] = new Card[3];
			for (int n = 0; n < 3; n++) {
				waitformsg[n] = true;
				inhand[n] = hands[3].getCard(n);
				ontable[n] = hands[3].getFaceUp(n);
			}
			SwapD swapD = new SwapD(sh, inhand, ontable);
			if (swapD.display()) {
				hands[3].swap(swapD.getInHand(), swapD.getOnTable());
				displayTable();
			}
			swapcount++;

			// if dealer is last to make the swap
			if (swapcount == 4) {
				sendDetails();
				nextTurn();
				displayTable();
			}

			// swap for ai players
			for (int n = 0; n < 3; n++)
				if (aiPlayer[n] != PlayerType.HUMAN)
					processSwap(ai.swap(hands[n], nine), 4, n);

		} else {// else start game
			nextTurn();
			displayTable();
		}
	}

	/*---------------------------------------------
	 * sending detail of other players to other players
	 * table layout
	 *     1
	 *  0     2
	      3 <- dealer
	 *--------------------------------------------*/
	private void sendDetails() {
		String command = "otherdetails:" + otherNames[1] + ":"
				+ hands[1].getFaceUp(0).getNumber() + ":"
				+ hands[1].getFaceUp(1).getNumber() + ":"
				+ hands[1].getFaceUp(2).getNumber() + ":" + otherNames[2] + ":"
				+ +hands[2].getFaceUp(0).getNumber() + ":"
				+ hands[2].getFaceUp(1).getNumber() + ":"
				+ hands[2].getFaceUp(2).getNumber() + ":" + playersName + ":"
				+ hands[3].getFaceUp(0).getNumber() + ":"
				+ +hands[3].getFaceUp(1).getNumber() + ":"
				+ hands[3].getFaceUp(2).getNumber() + ":";
		sendCommand(command, 0);

		command = "otherdetails:" + otherNames[2] + ":"
				+ hands[2].getFaceUp(0).getNumber() + ":"
				+ hands[2].getFaceUp(1).getNumber() + ":"
				+ hands[2].getFaceUp(2).getNumber() + ":" + playersName + ":"
				+ +hands[3].getFaceUp(0).getNumber() + ":"
				+ hands[3].getFaceUp(1).getNumber() + ":"
				+ hands[3].getFaceUp(2).getNumber() + ":" + otherNames[0] + ":"
				+ hands[0].getFaceUp(0).getNumber() + ":"
				+ +hands[0].getFaceUp(1).getNumber() + ":"
				+ hands[0].getFaceUp(2).getNumber() + ":";
		sendCommand(command, 1);

		command = "otherdetails:" + playersName + ":"
				+ hands[3].getFaceUp(0).getNumber() + ":"
				+ hands[3].getFaceUp(1).getNumber() + ":"
				+ hands[3].getFaceUp(2).getNumber() + ":" + otherNames[0] + ":"
				+ +hands[0].getFaceUp(0).getNumber() + ":"
				+ hands[0].getFaceUp(1).getNumber() + ":"
				+ hands[0].getFaceUp(2).getNumber() + ":" + otherNames[1] + ":"
				+ hands[1].getFaceUp(0).getNumber() + ":"
				+ +hands[1].getFaceUp(1).getNumber() + ":"
				+ hands[1].getFaceUp(2).getNumber() + ":";
		sendCommand(command, 2);
	}

	public void endConnection() {
		listen = false;

		if (listenSocket != null)
			try {
				listenSocket.close();
			} catch (IOException e) {
				sh.addMsg("Error Closing Listen " + e);
			}

		for (int n = 0; n < 3; n++) {
			if (gameSocket[n] != null) {
				out[n].println("end");
				try {
					gameSocket[n].close();
				} catch (IOException e2) {
					sh.addMsg("Error Closing Msg " + e2);
				}
			}
		}

		socketCount = 0;
		sh.addMsg("Game Connections Closed");
	}

	public void destroy() {
		gameID++;
		endConnection();
	}

	public void displayTable() {
		g.setColor(Color.black);
		g.fillRect(0, 0, 450, 550);
		g.setColor(Color.white);
		g.drawLine(0, 450, 450, 450);
		g.setColor(Color.red);
		g.drawRoundRect(355, 5, 90, 40, 15, 15);
		g.setColor(Color.white);
		g.drawString("Deck: " + decklength(), 365, 20);
		g.drawString("Pile: " + pilelength(), 365, 40);
		hands[3].showHand();

		g.drawRoundRect(5, 360, 90, 40, 15, 15);
		g.drawString("Name: " + otherNames[0], 10, 375);
		g.drawString("Cards: " + (hands[0].length() - 1), 10, 395);
		g.drawImage(pointer[1], 68, 380, sh);
		if (hands[0].getFaceUp(0) != null)
			hands[0].getFaceUp(0).drawSideWays(tableposition[0][0]);
		else if (hands[0].getFaceDown(0) != null)
			g.drawImage(backSW, (int) tableposition[0][0].getX(),
					(int) tableposition[0][0].getY(), sh);
		if (hands[0].getFaceUp(1) != null)
			hands[0].getFaceUp(1).drawSideWays(tableposition[0][1]);
		else if (hands[0].getFaceDown(1) != null)
			g.drawImage(backSW, (int) tableposition[0][1].getX(),
					(int) tableposition[0][1].getY(), sh);
		if (hands[0].getFaceUp(2) != null)
			hands[0].getFaceUp(2).drawSideWays(tableposition[0][2]);
		else if (hands[0].getFaceDown(2) != null)
			g.drawImage(backSW, (int) tableposition[0][2].getX(),
					(int) tableposition[0][2].getY(), sh);

		g.drawRoundRect(5, 5, 90, 40, 15, 15);
		g.drawString("Name: " + otherNames[1], 10, 20);
		g.drawString("Cards: " + (hands[1].length() - 1), 10, 40);
		g.drawImage(pointer[2], 70, 25, sh);

		if (hands[1].getFaceUp(0) != null)
			hands[1].getFaceUp(0).drawCard(tableposition[1][0]);
		else if (hands[1].getFaceDown(0) != null)
			g.drawImage(back, (int) tableposition[1][0].getX(),
					(int) tableposition[1][0].getY(), sh);
		if (hands[1].getFaceUp(1) != null)
			hands[1].getFaceUp(1).drawCard(tableposition[1][1]);
		else if (hands[1].getFaceDown(1) != null)
			g.drawImage(back, (int) tableposition[1][1].getX(),
					(int) tableposition[1][1].getY(), sh);
		if (hands[1].getFaceUp(2) != null)
			hands[1].getFaceUp(2).drawCard(tableposition[1][2]);
		else if (hands[1].getFaceDown(2) != null)
			g.drawImage(back, (int) tableposition[1][2].getX(),
					(int) tableposition[1][2].getY(), sh);

		g.drawRoundRect(355, 360, 90, 40, 15, 15);
		g.drawString("Name: " + otherNames[2], 360, 375);// 365
		g.drawString("Cards: " + (hands[2].length() - 1), 360, 395);// 365
		g.drawImage(pointer[1], 423, 380, sh);
		if (hands[2].getFaceUp(0) != null)
			hands[2].getFaceUp(0).drawSideWays(tableposition[2][0]);
		else if (hands[2].getFaceDown(0) != null)
			g.drawImage(backSW, (int) tableposition[2][0].getX(),
					(int) tableposition[2][0].getY(), sh);
		if (hands[2].getFaceUp(1) != null)
			hands[2].getFaceUp(1).drawSideWays(tableposition[2][1]);
		else if (hands[2].getFaceDown(1) != null)
			g.drawImage(backSW, (int) tableposition[2][1].getX(),
					(int) tableposition[2][1].getY(), sh);
		if (hands[2].getFaceUp(2) != null)
			hands[2].getFaceUp(2).drawSideWays(tableposition[2][2]);
		else if (hands[2].getFaceDown(2) != null)
			g.drawImage(backSW, (int) tableposition[2][2].getX(),
					(int) tableposition[2][2].getY(), sh);
		// drawing pile
		if (pile[0] != null) {
			// determining how many cards of the same value are ontop of each
			// other
			int top = 0;
			if (nine == true && pile[0].getValue() == 9) {
				top = 1;
				if (pile[1] != null)
					if (pile[1].getValue() == 9) {
						top = 2;
						if (pile[2] != null)
							if (pile[2].getValue() == 9)
								top = 3;
					}
			}
			int samecount = 1;
			for (int n = top + 1; n < top + 4; n++) {
				if (pile[n] == null)
					break;
				if (pile[n].getValue() == pile[top].getValue())
					samecount++;
				else
					break;
			}
			if (samecount == 1) {// one of a kind
				if (pile[top] != null)
					pile[top].drawCard(centre1);
			} else if (samecount == 2) {// 2 of a kind
				pile[top + 1].drawCard2((int) centre1.getX(),
						(int) centre1.getY() - 10);
				pile[top].drawCard2((int) centre1.getX(),
						(int) centre1.getY() + 10);
			} else if (samecount >= 3) {// 3 of a kind
				pile[top + 2].drawCard2((int) centre1.getX(),
						(int) centre1.getY() - 20);
				pile[top + 1].drawCard2((int) centre1.getX(),
						(int) centre1.getY());
				pile[top].drawCard2((int) centre1.getX(),
						(int) centre1.getY() + 20);
			}
			if (nine == true && pile[0].getValue() == 9)
				if (top == 1)// one nine
					pile[0].drawSideWays2((int) centre1.getX() - 15,
							(int) centre1.getY() + 40);
			if (top == 2) {// 2 nines
				pile[1].drawSideWays2((int) centre1.getX() - 15,
						(int) centre1.getY() + 40);
				pile[0].drawSideWays2((int) centre1.getX() - 15,
						(int) centre1.getY() + 50);
			}
			if (top == 3) {// 3 nines
				pile[2].drawSideWays2((int) centre1.getX() - 15,
						(int) centre1.getY() + 40);
				pile[1].drawSideWays2((int) centre1.getX() - 15,
						(int) centre1.getY() + 50);
				pile[0].drawSideWays2((int) centre1.getX() - 15,
						(int) centre1.getY() + 60);
			}
		} else if (burnt) {
			g.drawImage(burntPic, 130, 190, sh);
			burnt = false;
		}
		g.drawImage(pointer[whosturn], (int) pointerpoints[whosturn].getX(),
				(int) pointerpoints[whosturn].getY(), sh);
		if (sh.smallscreen())
			sh.scalepic();
		sh.repaint();
	}

	public void redeal() {

		if (socketCount >= 3) {

			for (int n = 0; n < 4; n++) {
				// setting scoreboard if game finishes early
				if (n < 3) {
					if (outofgame[n] == false)
						score.addScore(otherNames[n], position);
				} else {
					if (outofgame[n] == false)
						score.addScore(playersName, position);
				}

				outofgame[n] = false;
				if (n < 3)
					waitformsg[n] = false;
				hands[n].removeAll();
			}

			whosturn = 2;
			position = 1;
			swapcount = 0;

			for (int n = 0; n < 52; n++)
				pile[n] = null;
			for (int n = 0; n < 3; n++)
				sendCommand("reset:", n);
			deal();
		}
	}

	private int pilelength() {
		int cardCount = 0;
		for (int n = 0; n < 52; n++) {
			if (pile[n] == null)
				break;
			cardCount++;
		}
		return cardCount;
	}

	private int decklength() {
		int cardCount = 0;
		for (int n = 0; n < 52; n++) {
			if (deck[n] == null)
				break;
			cardCount++;
		}
		return cardCount;
	}

	private void outofgame(int playerout) {

		String name = "";
		// telling whos out of the game there are out
		if (playerout == 3) {// dealer out of game
			if (position == 1)
				sh.addMsg("Well done you have won the game your the first out !!");
			else if (position == 2)
				sh.addMsg("You've done alright you the second out of the game");
			else if (position == 3)
				sh.addMsg("Just made it, congrats your not a ShitHead !");
			name = playersName;
		} else {
			sendCommand("out:" + position + ":", playerout);
			name = otherNames[playerout];
			sh.addMsg(name + " is out of the game");
		}

		// updating scoreboard
		score.addScore(name, position);

		// sending message to everyone else
		for (int n = 0; n < 3; n++)
			if (n != playerout)
				sendCommand("otherout:" + name + ":", n);

		outofgame[playerout] = true;
		position++;

		// ending game if over
		if (position == 4) {
			gameID++;
			int wholost = 0;
			// finding out which player is still in the game
			for (int n = 0; n < 4; n++)
				if (!outofgame[n]) {
					wholost = n;
					outofgame[n] = true;
					break;
				}
			if (wholost == 3) {
				sh.addMsg("You Lost ShitHead !!!");
				sh.addMsg("Game Over");
				name = playersName;
				displayTable();
			} else {
				sendCommand("out:" + position + ":", wholost);
				name = otherNames[wholost];
				sh.addMsg(name + " is the Shithead");
				sh.addMsg("Game Over");
				whosturn = 3;
				displayTable();
			}

			// updating scoreboard
			score.addScore(name, 4);
			new ScoreThread();

			// sending message to everyone else
			for (int n = 0; n < 3; n++)
				if (otherNames[n] != name)
					sendCommand("lost:" + name + ":", n);
			outofgame[wholost] = true;
		}
	}

	public void cardSelection(int cardno) {
		Card card;
		String command;
		if (hands[3].getCard(0) == null) {// if player only has cards on table
			if (hands[3].getFaceUp(0) != null || hands[3].getFaceUp(1) != null
					|| hands[3].getFaceUp(2) != null) { // if cards still faceup
														// on table
				card = hands[3].getFaceUp(cardno);
				command = "othersturn:" + playersName + ":faceup:";
				if (isValidCard(card, command))
					hands[3].removeFaceUp(cardno);
			} else {// if only cards down
				card = hands[3].getFaceDown(cardno);
				command = "othersturn:" + playersName + ":facedown:";
				if (isValidCard(card, command)) {
					hands[3].removeFaceDown(cardno);
					// checking if player is out of the game
					if (hands[3].getFaceDown(0) == null
							&& hands[3].getFaceDown(1) == null
							&& hands[3].getFaceDown(2) == null)
						outofgame(3);
				} else {// player must pick up the deck if not valid
					sh.addMsg("The card you played was a "
							+ hands[3].getFaceDown(cardno).getStringValue()
							+ " you had to pick up the pile");
					hands[3].addCard(hands[3].getFaceDown(cardno));
					for (int n = 0; n < 3; n++)
						sendCommand(
								"othersturn:"
										+ playersName
										+ ":facedown:pickup:"
										+ hands[3].getFaceDown(cardno)
												.getNumber() + ":", n);
					for (int n = 0; n < 52; n++) {
						if (pile[n] == null)
							break;
						hands[3].addCard(pile[n]);
						pile[n] = null;
					}
					hands[3].removeFaceDown(cardno);
					nextTurn();
				}
			}
		} else {// if player still has cards in hand
			card = hands[3].getCard(cardno);
			command = "othersturn:" + playersName + ":";
			if (isValidCard(card, command))
				for (int n = 0; n < hands[3].length() - 1; n++)
					if (card.getNumber() == hands[3].getCard(n).getNumber()) {
						hands[3].removeCard(n);
						break;
					}
			// checking if player is out of the game
			if (hands[3].getFaceDown(0) == null
					&& hands[3].getFaceDown(1) == null
					&& hands[3].getFaceDown(2) == null
					&& hands[3].getCard(0) == null) {
				outofgame(3);
				if (whosturn == 3)
					nextTurn();
			}
		}
		// if players hand is less than 3 card and card still in deck give
		// another card to player
		while (deck[0] != null && hands[3].length() <= 3) {
			hands[3].addCard(deck[0]);
			// removing card from deck
			deck[51] = null;
			for (int w = 0; w < 51; w++)
				deck[w] = deck[w + 1];
		}
		displayTable();
	}

	private boolean checkformultiFaceUp(Card card) {
		// checking if card selection is valid
		if (pile[0] != null) {
			if (card.getValue() == 9 && nine == true) {
				// do nothing as valid card
			} else if (card.getValue() == 10 || card.getValue() == 2) {
				// do nothing as valid card
			} else if (nine == true && pile[0].getValue() == 9) {
				int count = 0;// determining the number of nines on top of pile
				for (int i = 0; i < 52; i++) {
					if (pile[i] == null)
						break;
					if (pile[i].getValue() == 9)
						count++;
					else
						break;
				}
				if (pile[count] != null) {
					if (card.getValue() == 9) {
						// do nothing as valid card
					} else if (seven == true && pile[count].getValue() == 7) {
						if (card.getValue() >= 7)
							return false;
					} else if (!(card.getValue() == 2 || card.getValue() == 10 || card
							.getValue() >= pile[count].getValue()))
						return false;
				}
			} else if (seven == true && pile[0].getValue() == 7) {
				if (card.getValue() >= 7)
					return false;
			} else if (!(card.getValue() == 2 || card.getValue() == 10 || card
					.getValue() >= pile[0].getValue()))
				return false;
		}

		// checking how many card of the same value as card played are in
		// players hand
		int amountinhand = 0;
		for (int n = 0; n < 3; n++) {
			if (hands[3].getFaceUp(n) != null)
				if (hands[3].getFaceUp(n).getValue() == card.getValue())
					amountinhand++;
		}
		if (amountinhand <= 1)
			return false;
		MultiCardD dialog = new MultiCardD(sh, amountinhand);
		int numbertoplay = dialog.getChoice();
		if (numbertoplay <= 1)
			return false;
		String command = "othersturn:" + playersName + ":faceup:multi:"
				+ numbertoplay + ":" + card.getNumber() + ":";
		addcardtopile(card);
		numbertoplay--;
		for (int n = 0; n < 3; n++)
			if (hands[3].getFaceUp(n) != null) {
				if (numbertoplay <= 0)
					break;
				if (card.getValue() == hands[3].getFaceUp(n).getValue()
						&& card.getNumber() != hands[3].getFaceUp(n)
								.getNumber()) {
					command = command.concat(hands[3].getFaceUp(n).getNumber()
							+ ":");
					addcardtopile(hands[3].getFaceUp(n));
					// storing which card are to be removed
					hands[3].removeFaceUp(n);
					numbertoplay--;
				}
			}
		// sending command
		for (int n = 0; n < 3; n++)
			sendCommand(command, n);
		// checking for 4 of a kind
		if (fourOfAKind(pile[3]) || pile[0].getValue() == 10) {
			burnt = true;
			for (int n = 0; n < 51; n++)
				pile[n] = null;
			sh.addMsg("You burn the pile is your turn again");
		} else {
			sh.setmyTurn(false);
			nextTurn();
		}
		return true;
	}

	private boolean checkformulti(Card card) {
		// checking if card selection is valid
		if (pile[0] != null) {
			if (nine == true && pile[0].getValue() == 9) {
				int count = 0;// determining the number of nines on top of pile
				for (int i = 0; i < 52; i++) {
					if (pile[i] == null)
						break;
					if (pile[i].getValue() == 9)
						count++;
					else
						break;
				}
				if (pile[count] != null) {
					if (card.getValue() == 9) {
						// do nothing as valid card
					} else if (seven == true && pile[count].getValue() == 7) {
						if (card.getValue() >= 7)
							return false;
					} else if (!(card.getValue() == 2 || card.getValue() == 10 || card
							.getValue() >= pile[count].getValue()))
						return false;
				}
			} else if (card.getValue() == 9 && nine == true) {
				// do nothing as valid card
			} else if (seven == true && pile[0].getValue() == 7) {
				if (card.getValue() >= 7)
					return false;
			} else if (!(card.getValue() == 2 || card.getValue() == 10 || card
					.getValue() >= pile[0].getValue()))
				return false;
		}

		// checking how many card of the same value as card played are in
		// players hand
		int amountinhand = 0;
		for (int n = 0; n < hands[3].length(); n++) {
			if (hands[3].getCard(n) == null)
				break;
			if (hands[3].getCard(n).getValue() == card.getValue())
				amountinhand++;
		}
		if (amountinhand <= 1)
			return false;
		MultiCardD dialog = new MultiCardD(sh, amountinhand);
		int numbertoplay = dialog.getChoice();
		if (numbertoplay <= 1)
			return false;
		String command = "othersturn:" + playersName + ":multi:" + numbertoplay
				+ ":" + card.getNumber() + ":";
		addcardtopile(card);
		numbertoplay--;
		int toberemovedcount = 0;
		int toberemoved[] = new int[3];
		for (int n = 0; n < 3; n++)
			toberemoved[n] = -1;
		for (int n = 0; n < hands[3].length() - 1; n++) {
			if (hands[3].getCard(n) == null)
				break;
			if (numbertoplay <= 0)
				break;
			if (card.getValue() == hands[3].getCard(n).getValue()
					&& card.getNumber() != hands[3].getCard(n).getNumber()) {
				command = command.concat(hands[3].getCard(n).getNumber() + ":");
				addcardtopile(hands[3].getCard(n));
				// storing which card are to be removed
				toberemoved[toberemovedcount] = hands[3].getCard(n).getNumber();
				toberemovedcount++;
				numbertoplay--;
			}
		}
		// removing card from hand
		for (int n = 0; n < 3; n++) {
			if (toberemoved[n] == -1)
				break;
			for (int i = 0; i < hands[3].length() - 1; i++)
				if (hands[3].getCard(i).getNumber() == toberemoved[n]) {
					hands[3].removeCard(i);
					break;
				}
		}
		// sending command
		for (int n = 0; n < 3; n++)
			sendCommand(command, n);
		// checking for 4 of a kind
		if (fourOfAKind(pile[3]) || pile[0].getValue() == 10) {
			burnt = true;
			for (int n = 0; n < 51; n++)
				pile[n] = null;
			// checking if player is out of game
			if (hands[3].getFaceDown(0) == null
					&& hands[3].getFaceDown(1) == null
					&& hands[3].getFaceDown(2) == null
					&& hands[3].getCard(0) == null)
				nextTurn();
			else
				// else there go again
				sh.addMsg("You burn the pile is your turn again");
		} else {
			sh.setmyTurn(false);
			nextTurn();
		}
		return true;
	}

	private void nextTurn() {
		whosturn++;
		if (whosturn >= 4)
			whosturn = 0;
		int loop = 0;// stop program becoming trapped in loop
		while (outofgame[whosturn] && loop < 5) {
			loop++;
			whosturn++;
			if (whosturn >= 4)
				whosturn = 0;
		}
		if (whosturn != 3) {
			waitformsg[whosturn] = true;
			if (aiPlayer[whosturn] == PlayerType.HUMAN) {
				sendCommand("yourturn:", whosturn);
			} else {
				new AIThread();
			}
		} else {
			canDealerPlay();
		}
	}

	public boolean fourOfAKind(Card card) {
		if (pile[0] == null || pile[1] == null || pile[2] == null
				|| card == null)
			return false;
		int top = pile[0].getValue();
		if (pile[1].getValue() == top && pile[2].getValue() == top
				&& card.getValue() == top)
			return true;
		return false;
	}

	public boolean isValidCard(Card card, String command) {
		int top = 0;
		boolean multi = false;
		if (hands[3].getCard(0) != null)
			multi = checkformulti(card);
		else if (hands[3].isFaceUp())
			multi = checkformultiFaceUp(card);

		if (multi)
			return true;

		if (card.getValue() == 2) {// 2 can be played at anytime
			cardAccepted(card, command);
			return true;
		} else if (card.getValue() == 10 || fourOfAKind(card)) {// pile is burn
																// and its
																// players turn
																// again
			burnt = true;
			for (int n = 0; n < 51; n++)
				pile[n] = null;
			if (hands[3].isFaceDown() == true || hands[3].length() > 1) {
				for (int n = 0; n < 3; n++)
					if (hands[3].length() > 1)
						sendCommand(command + "burn:", n);
					else
						sendCommand(command + card.getNumber() + ":", n);
				sh.addMsg("You have burnt the pile, its your turn again");
			} else {
				for (int n = 0; n < 3; n++)
					if (hands[3].length() > 1)
						sendCommand(command + "burn:", n);
					else
						sendCommand(command + card.getNumber() + ":", n);
				sh.setmyTurn(false);
				nextTurn();
			}
			return true;
		} else if (pile[0] == null) {
			cardAccepted(card, command);
			return true;
		} else if (nine == true && card.getValue() == 9) {
			cardAccepted(card, command);
			return true;
		}
		if (nine == true && pile[0].getValue() == 9) {
			for (int i = 0; i < 52; i++) {
				if (pile[i] == null) {
					cardAccepted(card, command);
					return true;
				}
				if (pile[i].getValue() == 9)
					top++;
				else
					break;
			}

		}
		if (seven == true && pile[top].getValue() == 7) {
			if (card.getValue() >= 7) {
				sh.addMsg("You Must Play Less Than a Seven");
				return false;
			} else {
				cardAccepted(card, command);
				return true;
			}
		}
		if (pile[top].getValue() <= card.getValue()) {
			cardAccepted(card, command);
			return true;
		}
		if (hands[3].isFaceUp() == true || hands[3].length() > 1) {// top
			sh.addMsg("You can't play a " + card.getStringValue()
					+ " please select another card");
		}
		return false;
	}

	private void cardAccepted(Card card, String command) {
		// sending move to other players
		for (int n = 0; n < 3; n++)
			sendCommand(command + card.getNumber() + ":", n);
		// adding card to pile
		for (int n = 51; n > 0; n--)
			pile[n] = pile[n - 1];
		pile[0] = card;
		sh.setmyTurn(false);
		nextTurn();
	}

	public void processTurn(String otherplayermsg, int commandlength) {
		boolean burn = false;
		// decode variable that came with message
		int varlength = 0;
		for (int n = commandlength + 1; n < otherplayermsg.length(); n++) {
			char extract = otherplayermsg.charAt(n);
			if (extract == (':')) {
				varlength = n;
				break;
			}
		}
		String variable = otherplayermsg
				.substring(commandlength + 1, varlength);
		if (variable.equals("pickup")) {// if other player picks up deck
			for (int n = 0; n < 52; n++) {
				if (pile[n] == null)
					break;
				hands[whosturn].addCard(pile[n]);
				pile[n] = null;
			}
			// sending move to other players
			for (int n = 0; n < 3; n++)
				if (n != whosturn)
					sendCommand("othersturn:" + otherNames[whosturn]
							+ ":pickup:", n);
			sh.addMsg(otherNames[whosturn] + " picked up the pile");
		} else if (variable.equals("faceup")) {// if player plays one of there
												// face up cards
			int varlength2 = 0;
			for (int n = varlength + 1; n < otherplayermsg.length(); n++) {
				char extract = otherplayermsg.charAt(n);
				if (extract == (':')) {
					varlength2 = n;
					break;
				}
			}
			String cardno = otherplayermsg.substring(varlength + 1, varlength2);
			if (cardno.equals("multi")) {
				burn = faceupMulti(otherplayermsg, varlength2);
			} else {
				int cardNo = 0;
				try {
					cardNo = Integer.parseInt(cardno);
				} catch (NumberFormatException b) {
					sh.addMsg("processTurn - variable to Int error: " + b);
				}
				// removing card from players hand and adding to pile
				for (int n = 0; n < 3; n++) {
					if (hands[whosturn].getFaceUp(n) != null)
						if (hands[whosturn].getFaceUp(n).getNumber() == cardNo) {
							if (hands[whosturn].getFaceUp(n).getValue() == 10
									|| fourOfAKind(hands[whosturn].getFaceUp(n)) == true) {// if
																							// card
																							// is
																							// 10
																							// burning
																							// the
																							// deck
								burnt = true;
								for (int i = 0; i < 52; i++)
									pile[i] = null;
								burn = true;
								sh.addMsg(otherNames[whosturn]
										+ " burnt the pile");
							} else {// else adding card to pile
								for (int i = 51; i > 0; i--)
									pile[i] = pile[i - 1];
								pile[0] = hands[whosturn].getFaceUp(n);
							}
							hands[whosturn].removeFaceUp(n);
							break;
						}
				}
				// sending move to other players
				for (int n = 0; n < 3; n++)
					if (n != whosturn)
						sendCommand("othersturn:" + otherNames[whosturn]
								+ ":faceup:" + cardno + ":", n);
			}

		} else if (variable.equals("facedown")) {// if player plays a facedown
													// card
			int varlength2 = 0;
			for (int n = varlength + 1; n < otherplayermsg.length(); n++) {
				char extract = otherplayermsg.charAt(n);
				if (extract == (':')) {
					varlength2 = n;
					break;
				}
			}
			String cardno = otherplayermsg.substring(varlength + 1, varlength2);
			if (cardno.equals("pickup")) {// card that was played facedown made
											// player pick up the deck
				int varlength3 = 0;// getting card that player played
				for (int n = varlength2 + 1; n < otherplayermsg.length(); n++) {
					char extract = otherplayermsg.charAt(n);
					if (extract == (':')) {
						varlength3 = n;
						break;
					}
				}
				String cardno2 = otherplayermsg.substring(varlength2 + 1,
						varlength3);
				int cardNo = 0;
				try {
					cardNo = Integer.parseInt(cardno2);
				} catch (NumberFormatException b) {
					sh.addMsg("processTurn facedown pickup- variable to Int error: "
							+ b);
				}
				for (int n = 0; n < 3; n++)
					if (hands[whosturn].getFaceDown(n) != null)
						if (hands[whosturn].getFaceDown(n).getNumber() == cardNo) {
							hands[whosturn].addCard(hands[whosturn]
									.getFaceDown(n));
							for (int i = 0; i < 52; i++) {
								if (pile[i] == null)
									break;
								hands[whosturn].addCard(pile[i]);
								pile[i] = null;
							}
							for (int i = 0; i < 3; i++)
								if (i != whosturn)
									sendCommand("othersturn:"
											+ otherNames[whosturn]
											+ ":facedown:pickup:" + cardNo
											+ ":", i);
							sh.addMsg(otherNames[whosturn] + " played a "
									+ Card.getCardStringValue(cardNo)
									+ " and had to pick up the deck");
							hands[whosturn].removeFaceDown(n);
							break;
						}
			} else {// facedown card didnt make player pick up the deck
				int cardNo = 0;
				try {
					cardNo = Integer.parseInt(cardno);
				} catch (NumberFormatException b) {
					sh.addMsg("processTurn - variable to Int error: " + b);
				}
				// removing card from players hand and adding to pile
				for (int n = 0; n < 3; n++) {
					if (hands[whosturn].getFaceDown(n) != null)
						if (hands[whosturn].getFaceDown(n).getNumber() == cardNo) {
							if (hands[whosturn].getFaceDown(n).getValue() == 10
									|| fourOfAKind(hands[whosturn]
											.getFaceDown(n)) == true) {// if
																		// card
																		// is 10
																		// burning
																		// the
																		// deck
								burnt = true;
								for (int i = 0; i < 52; i++)
									pile[i] = null;
								if (hands[whosturn].isFaceDown() == true
										|| hands[whosturn].length() > 1)
									burn = true;
								sh.addMsg(otherNames[whosturn]
										+ " burnt the pile");
							} else {// else adding card to pile
								for (int i = 51; i > 0; i--)
									pile[i] = pile[i - 1];
								pile[0] = hands[whosturn].getFaceDown(n);
							}
							hands[whosturn].removeFaceDown(n);
							break;
						}
				}
				// sending move to other players
				for (int n = 0; n < 3; n++)
					if (n != whosturn)
						sendCommand("othersturn:" + otherNames[whosturn]
								+ ":facedown:" + cardno + ":", n);
				// Check for out of game
				if (hands[whosturn].getFaceDown(0) == null
						&& hands[whosturn].getFaceDown(1) == null
						&& hands[whosturn].getFaceDown(2) == null
						&& hands[whosturn].getCard(0) == null)
					outofgame(whosturn);
			}

		} else if (variable.equals("multi")) {// if player plays a muliple cards
			// determining how many card where played
			int varlength2 = 0;
			for (int n = varlength + 1; n < otherplayermsg.length(); n++) {
				char extract = otherplayermsg.charAt(n);
				if (extract == (':')) {
					varlength2 = n;
					break;
				}
			}
			String numPlayedString = otherplayermsg.substring(varlength + 1,
					varlength2);
			// converting string to int for processing
			int numPlayed = 0;
			try {
				numPlayed = Integer.parseInt(numPlayedString);
			} catch (NumberFormatException b) {
				sh.addMsg("processTurn - multi - variable to Int error: " + b);
			}
			String command = "othersturn:" + otherNames[whosturn] + ":multi:"
					+ numPlayed + ":";
			for (int n = 0; n < numPlayed; n++) {
				varlength = varlength2;
				// determining how many card where played
				varlength2 = 0;
				for (int i = varlength + 1; i < otherplayermsg.length(); i++) {
					char extract = otherplayermsg.charAt(i);
					if (extract == (':')) {
						varlength2 = i;
						break;
					}
				}
				String cardnoString = otherplayermsg.substring(varlength + 1,
						varlength2);
				// converting string to int for processing
				int cardno = 0;
				try {
					cardno = Integer.parseInt(cardnoString);
				} catch (NumberFormatException b) {
					sh.addMsg("processTurn - multi - variable to Int error: "
							+ b);
				}
				int location = 0;
				for (int i = 0; i < hands[whosturn].length() - 1; i++)
					if (hands[whosturn].getCard(i).getNumber() == cardno) {
						location = i;
						break;
					}
				addcardtopile(hands[whosturn].getCard(location));
				command = command.concat(hands[whosturn].getCard(location)
						.getNumber() + ":");
				hands[whosturn].removeCard(location);
			}
			// if players hand is less than 3 card ands card still in deck give
			// another card to player
			while (deck[0] != null && hands[whosturn].length() <= 3) {
				hands[whosturn].addCard(deck[0]);
				sendCommand("getcard:" + deck[0].getNumber() + ":", whosturn);
				// removing card from deck
				deck[51] = null;
				for (int w = 0; w < 51; w++)
					deck[w] = deck[w + 1];
			}
			// sending turn to other players
			for (int n = 0; n < 3; n++)
				if (n != whosturn)
					sendCommand(command, n);
			// Check for out of game
			if (hands[whosturn].getFaceDown(0) == null
					&& hands[whosturn].getFaceDown(1) == null
					&& hands[whosturn].getFaceDown(2) == null
					&& hands[whosturn].getCard(0) == null)
				outofgame(whosturn);
			// checking for 4 of a kind else next players turn
			if (fourOfAKind(pile[3]) || pile[0].getValue() == 10) {
				if (!outofgame[whosturn])
					burnt = true;
				for (int n = 0; n < 51; n++)
					pile[n] = null;
				sh.addMsg(otherNames[whosturn] + " burn the pile");
				burn = true;
			}

		} else {// regular game play
				// converting string to int for processing
			int cardno = 0;
			try {
				cardno = Integer.parseInt(variable);
			} catch (NumberFormatException b) {
				sh.addMsg("processTurn - variable to Int error: " + b);
			}

			// removing card from players hand and adding to pile
			for (int n = 0; n < hands[whosturn].length() - 1; n++) {
				if (hands[whosturn].getCard(n) == null)
					break;
				if (hands[whosturn].getCard(n).getNumber() == cardno) {
					if (hands[whosturn].getCard(n).getValue() == 10
							|| fourOfAKind(hands[whosturn].getCard(n)) == true) {// if
																					// card
																					// is
																					// 10
																					// burning
																					// the
																					// deck
						burnt = true;
						for (int i = 0; i < 52; i++)
							pile[i] = null;
						burn = true;
						sh.addMsg(otherNames[whosturn] + " burnt the pile");
					} else {// else adding card to pile
						for (int i = 51; i > 0; i--)
							pile[i] = pile[i - 1];
						pile[0] = hands[whosturn].getCard(n);
					}
					hands[whosturn].removeCard(n);
					break;
				}
			}

			// if players hand is less than 3 card ands card still in deck give
			// another card to player
			if (deck[0] != null && hands[whosturn].length() <= 3) {
				hands[whosturn].addCard(deck[0]);
				sendCommand("getcard:" + deck[0].getNumber() + ":", whosturn);
				// removing card from deck
				deck[51] = null;
				for (int w = 0; w < 51; w++)
					deck[w] = deck[w + 1];
			}

			if (burn) {
				// sending move to other players
				for (int n = 0; n < 3; n++)
					if (n != whosturn)
						sendCommand("othersturn:" + otherNames[whosturn]
								+ ":burn:", n);
			} else {
				// sending move to other players
				for (int n = 0; n < 3; n++)
					if (n != whosturn)
						sendCommand("othersturn:" + otherNames[whosturn] + ":"
								+ cardno + ":", n);
			}

			// Check for out of game
			if (hands[whosturn].getFaceDown(0) == null
					&& hands[whosturn].getFaceDown(1) == null
					&& hands[whosturn].getFaceDown(2) == null
					&& hands[whosturn].getCard(0) == null)
				outofgame(whosturn);

		}
		if (!burn && position != 4) {
			if (whosturn != 3)
				waitformsg[whosturn] = false;
			nextTurn();
		}
		displayTable();
	}

	// returns true if pile burnt, false if not burnt
	private boolean faceupMulti(String otherplayermsg, int varlength) {
		// determining how many card where played
		int varlength2 = 0;
		for (int n = varlength + 1; n < otherplayermsg.length(); n++) {
			char extract = otherplayermsg.charAt(n);
			if (extract == (':')) {
				varlength2 = n;
				break;
			}
		}
		String numPlayedString = otherplayermsg.substring(varlength + 1,
				varlength2);
		// converting string to int for processing
		int numPlayed = 0;
		try {
			numPlayed = Integer.parseInt(numPlayedString);
		} catch (NumberFormatException b) {
			sh.addMsg("processTurn - multi - variable to Int error: " + b);
		}
		String command = "othersturn:" + otherNames[whosturn]
				+ ":faceup:multi:" + numPlayed + ":";
		for (int n = 0; n < numPlayed; n++) {
			varlength = varlength2;
			// determining how many card where played
			varlength2 = 0;
			for (int i = varlength + 1; i < otherplayermsg.length(); i++) {
				char extract = otherplayermsg.charAt(i);
				if (extract == (':')) {
					varlength2 = i;
					break;
				}
			}
			String cardnoString = otherplayermsg.substring(varlength + 1,
					varlength2);
			// converting string to int for processing
			int cardno = 0;
			try {
				cardno = Integer.parseInt(cardnoString);
			} catch (NumberFormatException b) {
				sh.addMsg("processTurn - multi - variable to Int error: " + b);
			}
			int location = 0;
			for (int i = 0; i < 3; i++)
				if (hands[whosturn].getFaceUp(i) != null)
					if (hands[whosturn].getFaceUp(i).getNumber() == cardno) {
						location = i;
						break;
					}
			addcardtopile(hands[whosturn].getFaceUp(location));
			command = command.concat(hands[whosturn].getFaceUp(location)
					.getNumber() + ":");
			hands[whosturn].removeFaceUp(location);
		}
		// sending turn to other players
		for (int n = 0; n < 3; n++)
			if (n != whosturn)
				sendCommand(command, n);
		// checking for 4 of a kind else next players turn
		if (fourOfAKind(pile[3]) || pile[0].getValue() == 10) {
			burnt = true;
			for (int n = 0; n < 51; n++)
				pile[n] = null;
			sh.addMsg(otherNames[whosturn] + " burn the pile");
			return true;
		}
		return false;
	}

	private void addcardtopile(Card card) {
		// adding card to pile
		for (int i = 51; i > 0; i--)
			pile[i] = pile[i - 1];
		pile[0] = card;
	}

	private void canDealerPlay() {
		// testing is player has a card they can play
		if (pile[0] != null) {
			int top = 0;
			boolean canplay = false;
			if (hands[3].getCard(0) == null) {// if player only has card on the
												// table
				if (hands[3].isFaceUp())// if player has faceup card on the
										// table
				{
					for (int n = 0; n < 3; n++) {
						if (hands[3].getFaceUp(n) != null) {
							if (nine == true && pile[0].getValue() == 9) {
								top = 0;
								for (int i = 0; i < 52; i++) {
									if (pile[i] == null) {
										canplay = true;
										break;
									}
									if (pile[i].getValue() == 9)
										top++;
									else
										break;
								}
							}
							if (canplay)
								break;
							if (seven == true && pile[top].getValue() == 7
									&& hands[3].getFaceUp(n).getValue() < 7) {
								canplay = true;
								break;
							} else if (hands[3].getFaceUp(n).getValue() == 2
									|| hands[3].getFaceUp(n).getValue() == 10) {
								canplay = true;
								break;
							} else if (nine == true
									&& hands[3].getFaceUp(n).getValue() == 9) {
								canplay = true;
								break;
							} else if (seven != true
									|| pile[top].getValue() != 7) {
								if (pile[top].getValue() <= hands[3].getFaceUp(
										n).getValue()) {
									canplay = true;
									break;
								}
							}
						}
					}
				} else
					// if player only has facedown cards
					canplay = true;
			} else {
				for (int n = 0; n < hands[3].length() - 1; n++) {
					if (hands[3].getCard(n) == null)
						break;
					if (nine == true && pile[0].getValue() == 9) {
						top = 0;
						for (int i = 0; i < 52; i++) {
							if (pile[i] == null) {
								canplay = true;
								break;
							}
							if (pile[i].getValue() == 9)
								top++;
							else
								break;
						}

					}
					if (canplay)
						break;
					if (hands[3].getCard(n).getValue() == 2
							|| hands[3].getCard(n).getValue() == 10) {
						canplay = true;
						break;
					}
					if (nine == true && hands[3].getCard(n).getValue() == 9) {
						canplay = true;
						break;
					}
					if (seven == true && pile[top].getValue() == 7
							&& hands[3].getCard(n).getValue() < 7) {
						canplay = true;
						break;
					} else if (seven != true || pile[top].getValue() != 7) {
						if (pile[top].getValue() <= hands[3].getCard(n)
								.getValue()) {
							canplay = true;
							break;
						}
					}
				}
			}
			if (canplay) {
				// sh.addMsg("Its Your Turn");
				sh.setmyTurn(true);
			} else {// cant play then must pick up the pile
				sh.addMsg("The card played was a " + pile[top].getStringValue()
						+ " you had to pick up the pile");
				for (int n = 0; n < 52; n++) {
					if (pile[n] == null)
						break;
					hands[3].addCard(pile[n]);
					pile[n] = null;
				}
				// sending move to other players
				for (int n = 0; n < 3; n++)
					sendCommand("othersturn:" + playersName + ":pickup:", n);
				nextTurn();
				displayTable();
			}
		} else {
			// sh.addMsg("Its Your Turn");
			sh.setmyTurn(true);
		}
	}

	private void processSwap(String otherplayermsg, int commandlength,
			int playerNumber) {
		if (!otherplayermsg.equals("error")) {
			int varlength;
			Card inhand[] = new Card[3];
			Card ontable[] = new Card[3];
			for (int r = 0; r < 2; r++)
				for (int n = 0; n < 3; n++) {
					varlength = commandlength;
					// determining how many card where played
					commandlength = 0;
					for (int i = varlength + 1; i < otherplayermsg.length(); i++) {
						char extract = otherplayermsg.charAt(i);
						if (extract == (':')) {
							commandlength = i;
							break;
						}
					}
					String cardnoString = otherplayermsg.substring(
							varlength + 1, commandlength);
					int cardno = 0;
					try {
						cardno = Integer.parseInt(cardnoString);
					} catch (NumberFormatException b) {
						sh.addMsg("processSwap - error - variable to Int error: "
								+ b);
					}
					Card card = new Card(cardno, cardspic, sh, g);
					if (r == 0)
						inhand[n] = card;
					else
						ontable[n] = card;
				}
			hands[playerNumber].swap(inhand, ontable);
		}
		waitformsg[playerNumber] = false;
		swapcount++;
		if (swapcount == 4) {
			sendDetails();
			nextTurn();
			displayTable();
		}

	}

	class WaitforMsg implements Runnable {

		Thread wt; // Wait Thread
		int socketNumber; // With of the 3 sockets is this socket listening to ?
		boolean socketOK = true;
		boolean haveswapped = false;

		WaitforMsg(int socketNumber) {
			this.socketNumber = socketNumber;

			wt = new Thread(this, "Wait");
			wt.start(); // Starting thread
		}

		public void run() {
			do {
				String otherplayermsg = "Message Error";
				try {
					otherplayermsg = in[socketNumber].readLine();
				} catch (IOException e) {
					sh.addMsg("Read Error: " + e);
					disconnect();
				}

				if (socketOK) {
					if (otherplayermsg == null) {
						disconnect();
					} else if (otherplayermsg.equals("end")) {
						disconnect();
					} else {
						if (waitformsg[socketNumber]) {
							int commandlength = 0;
							// decode message and perform function required
							for (int n = 0; n < otherplayermsg.length(); n++) {
								char extract = otherplayermsg.charAt(n);
								if (extract == (':')) {
									commandlength = n;
									break;
								}
							}
							String command = otherplayermsg.substring(0,
									commandlength);

							if (command.equals("turn"))
								processTurn(otherplayermsg, commandlength);

							if (command.equals("swap")) {
								processSwap(otherplayermsg, commandlength,
										socketNumber);
								haveswapped = true;
							}

						}
					}
				}

			} while (listen == true && socketOK == true);
			sh.addMsg("Player "
					+ otherNames[socketNumber]
					+ " Game Socket Has Disconnected \nReplacing player with GameAI");
			sendMsg("Player "
					+ otherNames[socketNumber]
					+ " Game Socket Has Disconnected Replacing player with GameAI");
		}

		private void disconnect() {
			try {
				gameSocket[socketNumber].close();
			} catch (IOException e) {
				sh.addMsg("Error Closing Listen " + e);
			}
			gameSocket[socketNumber] = null;
			in[socketNumber] = null;
			out[socketNumber] = null;
			aiPlayer[socketNumber] = PlayerType.SET_STRATEGY;
			if (!haveswapped)
				swapcount++;
			if (whosturn == socketNumber)
				new AIThread();
			socketOK = false;
		}

	}

	class ListenThread implements Runnable {

		Thread lt; // Listen Thread

		ListenThread() {
			lt = new Thread(this, "Listen");
			lt.start(); // Starting thread
		}

		public void run() {
			// Opening listening Socket
			listenSocket = null;
			try {
				listenSocket = new ServerSocket(4445);
			} catch (IOException e) {
				sh.addMsg("Could not listen " + e);
				return;
			}

			boolean endlook = false;
			// Waiting for connection
			do {
				gameSocket[socketCount] = null;
				try {
					gameSocket[socketCount] = listenSocket.accept();
				} catch (IOException e2) {
					// sh.addMsg("Error Accept " + e2);//removing error notice
					// as apears when game is started early
					endlook = true;
				}

				if (!endlook) {
					try {
						out[socketCount] = new PrintWriter(
								gameSocket[socketCount].getOutputStream(), true);
						in[socketCount] = new BufferedReader(
								new InputStreamReader(
										gameSocket[socketCount]
												.getInputStream()));
					} catch (IOException e) {
						sh.addMsg("Error Out / In problem." + e);
					}
					try {
						otherNames[socketCount] = in[socketCount].readLine();
						score.addName(otherNames[socketCount]);
					} catch (IOException e3) {
						sh.addMsg("Getting Otherplayers Name Error " + e3);
					}
					// checking if players name matches name of any of the other
					// players
					for (int n = 0; n < socketCount; n++)
						if (otherNames[socketCount].equals(otherNames[n]))
							otherNames[socketCount] = otherNames[socketCount]
									.concat("-2");

					if (otherNames[socketCount].equals(playersName))
						otherNames[socketCount] = otherNames[socketCount]
								.concat("-2");

					out[socketCount].println(playersName);
					out[socketCount].println(fastgame);
					out[socketCount].println(seven);
					out[socketCount].println(nine);
					out[socketCount].println(swap);
					new WaitforMsg(socketCount);
					socketCount++;
				}
			} while (listen == true && socketCount < 3 && endlook != true);

			sh.addMsg("No longer listening for Game Connections");
			if (listenSocket != null)
				try {
					listenSocket.close();
				} catch (IOException e) {
					sh.addMsg("Error Closing Listen " + e);
				}
			deal();
		}

	}

	class AIThread implements Runnable {

		Thread ait; // AI Thread
		int implaying;
		int currentID;

		AIThread() {
			ait = new Thread(this, "AI");
			ait.start(); // Starting thread
			implaying = (int) whosturn;
			currentID = (int) gameID;
		}

		public void run() {

			try {// pause before move played
				Thread.sleep(aipause);
			} catch (Exception e) {
				sh.addMsg("AI thead sleep error " + e);
			}

			// Using gameAI to process move
			String command;
			if (aiPlayer[whosturn] == PlayerType.REINFORCEMENT_LEARNING) {
				command = rlAI.basicMove(hands[whosturn], pile, pilelength());
			} else {
				command = ai.basicMove(hands[whosturn], pile, pilelength());
			}

			// if gameAI generates valid move, process move
			if (!command.equals("error") && implaying == whosturn
					&& currentID == gameID)
				processTurn(command, 4);

			if (implaying == whosturn && currentID == gameID)
				run();

		}
	}

	// shows scoreboard
	class ScoreThread implements Runnable {

		Thread st; // Score Thread

		ScoreThread() {
			st = new Thread(this, "Score");
			st.start(); // Starting thread
		}

		public void run() {
			score.display();
		}
	}
	
	enum PlayerType {
		HUMAN, SET_STRATEGY, REINFORCEMENT_LEARNING
	}

}